import { isArray } from '../shared';

/**
 * 该方法类似_.set，除了接受updater以生成要设置的值。使用_.updateWith来自定义生成的新path。updater调用1个参数：(value)。
 * @param object 要修改的对象。
 * @param path 要设置属性的路径。
 * @param updater 用来生成设置值的函数。
 * @returns 返回 object 
 * @example
 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
 * update(object, 'a[0].b.c', function(n) { return n * n; });
 * console.log(object.a[0].b.c); // => 9
 * 
 * update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; });
 * console.log(object.x[0].y.z); // => 0
 */
export function update(object: object, path: string | string[], updater: Function) :object {
    const ARRAY_IDENT = ']'
    const PATTERN = /\[|\./
    const SQUARE_BRACKETS = /[\[\]]/g

    let actObject = object
    const objectPaths = isArray(path) ? path : path.split(PATTERN)
    const keyIsHasArrIdent = (key: string) => key.includes(ARRAY_IDENT)
    const wipeSquareBrackets = (str: string) => str.replace(SQUARE_BRACKETS, '')
    const assignment = (obj: object, key: string | number, value: any) => 
        isArray(obj[key]) ? obj[key].push(value) : (obj[key] = value)

    for (let i = 0; i < objectPaths.length; i++) {
        const key = keyIsHasArrIdent(objectPaths[i]) ? Number(wipeSquareBrackets(objectPaths[i])) : objectPaths[i]
        const nextKey = objectPaths[i + 1] || ''
        const nextDefaultValue = keyIsHasArrIdent(nextKey) ? [] : {}
        const isLast = i === objectPaths.length - 1
        
        // 最后一步
        if(isLast) {
            assignment(actObject, key, updater(actObject[key]))
            break
        }
        
        // 没有值 => 创建
        if(!(key in actObject)) {
            assignment(actObject, key, nextDefaultValue)
        }
        actObject = actObject[key]
    }

    return object
}